<?php

namespace app\common\service;

use OSS\Credentials\StaticCredentialsProvider;
use OSS\OssClient;
use Swoole\Coroutine\Channel;
use Swoole\Coroutine\WaitGroup;
use think\App;
use think\facade\Log;
use think\Service;
use ZipArchive;

class ExportService extends Service
{
    public string $message;
    public int $line;
    public array $data;
    public function __construct(App $app)
    {
        parent::__construct($app);
    }

    /**
     * 打包本地文件
     * @param $data
     * @return bool
     */
    public function pack($data): bool
    {
        try {
            $zip = new ZipArchive();
            $ext = '.zip';
            $save_name = './export/pack' . date('Ym') . '/' . date('d') . '_' . time() . uniqid();
            $file_path = dirname($save_name . $ext);
            folder_build($file_path);
            $zipFileName = $save_name . $ext; // 压缩包文件名
            if ($zip->open($zipFileName, ZipArchive::CREATE) !== TRUE)
                throw new \Exception('无法创建压缩文件');

            $wg = new WaitGroup();
            $pool = new Channel(10); // 限制最大并发为10
            $chunks = array_chunk($data, 30); // 每批30条
            $i = 0;
            foreach ($chunks as $chunk) {
                foreach ($chunk as $item) {
                    if (empty($item)) {
                        continue;
                    }
                    $wg->add(); // 添加任务计数
                    go(function () use ($pool, $item,$zip,$wg,$i) {
                        try {
                            $pool->push(true); // 占用一个协程槽位
                            // 流式获取OSS文件内容
                            // 直接写入ZIP不落盘
                            $zip->addFile($item);
                        } catch (\Throwable $e) {
                            Log::channel('go')->error('压缩文件: '.$e->getMessage());
                        } finally {
                            $pool->pop(); // 释放协程槽位
                            $wg->done();
                        }
                    });
                    $i++;
                }
                $wg->wait(); // 每批完成后等待
            }
            $zip->close();
            $this->data = [
                'zipFileName' => $save_name . $ext
            ];
            return true;
        } catch (\Exception $e) {
            $this->message = $e->getMessage();
            $this->line = $e->getLine();
            return false;
        }

    }

    /**
     * 流式压缩OSS文件到本地ZIP包
     * @param $data
     * @return bool
     */
    public function streamCompress($data): bool
    {
        try {
            $ext = '.zip';
            $save_name = './export/pack' . date('Ym') . '/' . date('d') . '_' . time() . uniqid();
            $file_path = dirname($save_name . $ext);
            folder_build($file_path);
            $zip = new ZipArchive();
            if ($zip->open($save_name . $ext, ZipArchive::CREATE) !== TRUE) {
                throw new \Exception('无法创建ZIP文件');
            }
            // 初始化OSS客户端
            $config     = config('app.oss');
            // 获取AK和SK信息
            $accessKeyId = $config['access_key_id'];
            $accessKeySecret = $config['access_key_secret'];
            $provider = new StaticCredentialsProvider($accessKeyId,$accessKeySecret);
            $endpoint = $config['upload_url'];
            $ossConfig = array(
                "provider" => $provider,
                "endpoint" => $endpoint,
                "signatureVersion" => OssClient::OSS_SIGNATURE_VERSION_V4,
                "region"=> "cn-beijing"
            );
            $ossClient = new OssClient($ossConfig);
            $wg = new WaitGroup();
            $pool = new Channel(10); // 限制最大并发为10
            $chunks = array_chunk($data, 30); // 每批30条
            foreach ($chunks as $chunk) {
                foreach ($chunk as $item) {
                    if (empty($item)) {
                        continue;
                    }
                    $wg->add(); // 添加任务计数
                    $item = str_replace($config['static_url'].'/','',$item);
                    go(function () use ($pool, $ossClient, $config, $item,$zip,$wg) {
                        try {
                            $pool->push(true); // 占用一个协程槽位
                            // 流式获取OSS文件内容
                            $stream = $ossClient->getObject(
                                $config['bucket_name'],
                                $item
                            );
                            // 直接写入ZIP不落盘
                            $zip->addFromString(basename($item), $stream);
                        } catch (\Throwable $e) {
                            Log::channel('go')->error('压缩文件: '.$e->getMessage());
                        } finally {
                            $pool->pop(); // 释放协程槽位
                            $wg->done();
                        }
                    });
                }
                $wg->wait(); // 每批完成后等待
            }
            $zip->close();
            $this->data = [
                'zipFileName' => oss($save_name . $ext, $ext)
            ];
            return true;
        } catch (\Exception $e) {
            $this->message = $e->getMessage();
            $this->line = $e->getLine();
            return false;
        }
    }
}